<template>
	<view class="u-tooltip" :style="[$u.addStyle(customStyle)]">
		<u-overlay :show="showTooltip && tooltipTop !== -10000 && overlay"
			customStyle="backgroundColor: rgba(0, 0, 0, 0)" @click="overlayClickHandler"></u-overlay>
		<view class="u-tooltip__wrapper">
			<view class="u-tooltip__wrapper__text" :id="textId" :ref="textId" :userSelect="false" :selectable="false"
				@tap.stop="longpressHandler">
				<u-icon color="#22222" name="info-circle" size="36rpx"></u-icon>
			</view>
			<u-transition mode="fade" :show="showTooltip" duration="150" :customStyle="{
					position: 'absolute', 
					top: $u.addUnit(tooltipTop),
					zIndex: zIndex,
					...tooltipStyle
				}">
				<view class="u-tooltip__wrapper__popup" :id="tooltipId" :ref="tooltipId">
					<view class="u-tooltip__wrapper__popup__indicator" :style="[indicatorStyle, {
							width: $u.addUnit(indicatorWidth),
							height: $u.addUnit(indicatorWidth),
							backgroundColor: bgColor,
						}]">
					</view>
					<view class="u-tooltip__wrapper__popup__list" :style="{
							width: $u.addUnit($u.getPx(width)),
							backgroundColor: bgColor,
						}">
						<slot>{{text}}</slot>
					</view>
				</view>
			</u-transition>
		</view>
	</view>
</template>

<script>
	/**
	 * @description 
	 * @property {String | Number}	text		需要显示的提示文字
	 * @property {String | Number}	size		文本大小（默认 14 ）
	 * @property {String}			color		字体颜色（默认 '#606266' ）
	 * @property {String}			bgColor		弹出提示框时，文本的背景色（默认 'transparent' ）
	 * @property {String}			direction	弹出提示的方向，top-上方，bottom-下方（默认 'top' ）
	 * @property {String | Number}	zIndex		弹出提示的z-index，nvue无效（默认 10071 ）
	 * @property {Boolean}			overlay		是否显示透明遮罩以防止触摸穿透（默认 true ）
	 * @property {Object}			customStyle	定义需要用到的外部样式
	 * 
	 * @event {Function} 
	 * @example 
	 */
	export default {
		name: 'u-tooltip',
		mixins: [uni.$u.mpMixin, uni.$u.mixin],
		props: {
			// 需要显示的提示文字
			text: {
				type: [String, Number],
				default: uni.$u.props.tooltip.text
			},
			// 文本大小
			size: {
				type: [String, Number],
				default: uni.$u.props.tooltip.size
			},
			// 文本宽度
			width: {
				type: [String, Number],
				default: '400rpx'
			},
			// 字体颜色
			color: {
				type: String,
				default: uni.$u.props.tooltip.color
			},
			// 弹出提示框时，文本的背景色
			bgColor: {
				type: String,
				default: '#fff'
			},
			// 弹出提示的方向，top-上方，bottom-下方
			direction: {
				type: String,
				default: uni.$u.props.tooltip.direction
			},
			// 弹出提示的z-index，nvue无效
			zIndex: {
				type: [String, Number],
				default: uni.$u.props.tooltip.zIndex
			},
			// 是否显示透明遮罩以防止触摸穿透
			overlay: {
				type: Boolean,
				default: true
			},
		},
		data() {
			return {
				// 是否展示气泡
				showTooltip: false,
				// 生成唯一id，防止一个页面多个组件，造成干扰
				textId: uni.$u.guid(),
				tooltipId: uni.$u.guid(),
				// 初始时甚至为很大的值，让其移到屏幕外面，为了计算元素的尺寸
				tooltipTop: -10000,
				// 气泡的位置信息
				tooltipInfo: {
					width: 0,
					left: 0
				},
				// 文本的位置信息
				textInfo: {
					width: 0,
					left: 0
				},
				// 三角形指示器的样式
				indicatorStyle: {},
				// 气泡在可能超出屏幕边沿范围时，重新定位后，距离屏幕边沿的距离
				screenGap: 12,
				// 三角形指示器的宽高，由于对元素进行了角度旋转，精确计算指示器位置时，需要用到其尺寸信息
				indicatorWidth: 14,

				tooltipStyle: {},
			}
		},
		watch: {
			propsChange() {
				this.getElRect()
			}
		},
		computed: {
			// 特别处理H5的复制，因为H5浏览器是自带系统复制功能的，在H5环境
			// 当一些依赖参数变化时，需要重新计算气泡和指示器的位置信息
			propsChange() {
				return [this.text]
			},
		},
		mounted() {
			this.getElRect()
		},
		methods: {
			// 长按触发事件
			async longpressHandler() {
				this.tooltipTop = 0
				this.showTooltip = true
			},
			// 点击透明遮罩
			overlayClickHandler() {
				this.showTooltip = false
			},
			// 查询内容高度
			queryRect(ref) {
				// $uGetRect为uView自带的节点查询简化方法，详见文档介绍：https://www.uviewui.com/js/getRect.html
				// 组件内部一般用this.$uGetRect，对外的为uni.$u.getRect，二者功能一致，名称不同
				return new Promise(resolve => {
					this.$uGetRect(`#${ref}`).then(size => {
						resolve(size)
					}).catch(_ => {
						resolve({
							bottom: 0,
							height: 0,
							id: "",
							left: 0,
							right: 0,
							top: 0,
							width: 0,
						})
					})
				})
			},
			getTooltipStyle() {
				const style = {
						transform: `translateY(${this.direction === 'top' ? '-100%' : (this.textInfo.height + 10) + 'px'})`,
					},
					sys = uni.$u.sys(),
					getPx = uni.$u.getPx,
					addUnit = uni.$u.addUnit

				if (this.tooltipInfo.width / 2 > this.textInfo.left + this.textInfo.width / 2 - this.screenGap) {
					this.indicatorStyle = {}
					style.left = `-${addUnit(this.textInfo.left - this.screenGap)}`
					this.indicatorStyle.left = addUnit(this.textInfo.width / 2 - getPx(style.left) - this.indicatorWidth /
						2)
				} else if (this.tooltipInfo.width / 2 > sys.windowWidth - this.textInfo.right + this.textInfo.width / 2 -
					this.screenGap) {
					this.indicatorStyle = {}
					style.right = `-${addUnit(sys.windowWidth - this.textInfo.right - this.screenGap)}`
					this.indicatorStyle.right = addUnit(this.textInfo.width / 2 - getPx(style.right) - this
						.indicatorWidth / 2)
				} else {
					const left = Math.abs(this.textInfo.width / 2 - this.tooltipInfo.width / 2)
					style.left = this.textInfo.width > this.tooltipInfo.width ? addUnit(left) : -addUnit(left)
					this.indicatorStyle = {
						left: addUnit(this.tooltipInfo.width / 2 - this.textInfo.width / 2)
					}
				}
				if (this.direction === 'top') {
					style.marginTop = '-10px'
					this.indicatorStyle.bottom = '-4px'
				} else {
					style.marginBottom = '-10px'
					this.indicatorStyle.top = '-4px'
				}
				this.tooltipStyle = style
			},
			// 元素尺寸
			getElRect() {
				// 调用之前，先将指示器调整到屏幕外，方便获取尺寸
				this.showTooltip = true
				this.tooltipTop = -10000
				uni.$u.sleep(500).then(() => {
					this.queryRect(this.textId).then(size => {
						this.textInfo = size
						this.queryRect(this.tooltipId).then(size => {
							this.tooltipInfo = size
							// 获取气泡尺寸之后，将其隐藏，为了让下次切换气泡显示与隐藏时，有淡入淡出的效果
							this.showTooltip = false
							this.getTooltipStyle()
						})
					})
				})
			},
		}
	}
</script>

<style lang="scss" scoped>
	@mixin flex($direction: row) {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: $direction;
	}

	.u-tooltip {
		position: relative;

		&__wrapper {
			@include flex;
			justify-content: center;

			&__popup {
				&__list {
					border-radius: 16rpx;
					padding: 16rpx;
					overflow: hidden;
					border: 2rpx solid $u-border-color;
					box-shadow: $cc-shadow;
				}

				&__indicator {
					box-shadow: $cc-shadow;
					border: 2rpx solid $u-border-color;
					position: absolute;
					width: 14px;
					height: 14px;
					bottom: -4px;
					transform: rotate(45deg);
					border-radius: 4rpx;
					z-index: -1;
				}
			}
		}
	}
</style>